package com.quantum.remoting.service
{
	import com.quantum.remoting.ResultHandler;

	
	import com.quantum.logging.LogFactory;
	import com.quantum.util.StringUtils;
	
	import com.quantum.remoting.event.OperationReturnEvent;
	
	import com.quantum.remoting.proxy.RemoteObjectServiceProxy;
	
	import flash.utils.Proxy;
	import flash.events.Event;
	
	import mx.core.FlexGlobals;
	import mx.logging.ILogger;
	import mx.rpc.AbstractOperation;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import com.quantum.remoting.OperationInvocation;

	public class AbstractRemoteService
	{

		private static var log:ILogger=LogFactory.getLogger(AbstractRemoteService);

		private var _enabled:Boolean=true;
		
		private var _service:*;

		private var _resultHandler:ResultHandler;

		private var _operationInvocation:OperationInvocation;

		public function AbstractRemoteService(serviceName:String, channelUri:String=null, secure:Object=null)//, useAMF:Boolean=true
		{
			FlexGlobals.topLevelApplication.dispatchEvent(ObjectEvent.newAddition(this));
			FlexGlobals.topLevelApplication.dispatchEvent(ObjectEvent.newRemoval(this));

			_service = initService(serviceName, secure);
			
//			_service=useAMF ? RemoteObjectService.getInstance(serviceName, secure) : RemoteHessianService.getInstance(serviceName);
//         _service = RemoteService.getInstance(serviceName, secure);
			if (_service == null)
			{
				log.error("Failed to instantiate service: " + serviceName);
				return;
			}

			if (!StringUtils.isEmpty(channelUri))
			{
				_service.serviceChannelUri=channelUri;
			}
			_operationInvocation=new OperationInvocation();
		}

		public function initService(serviceName:String, secure:Object=null):*
		{
			return null;
		}

		
		/**
		 * @private
		 */
		[Injectable]
		public function get resultHandler():ResultHandler
		{
			return _resultHandler;
		}

		public function set resultHandler(value:ResultHandler):void
		{
			_resultHandler=value;
		}
		

		/**
		 * If <code>enabled</code> is <code>false</code>, all client requests will be
		 * blocked. And a null OperationReturnEvent will <code>null</code> result will be
		 * returned.
		 */
		public function get enabled():Boolean
		{
			return this._enabled;
		}

		public function set enabled(value:Boolean):void
		{
			this._enabled=value;
		}

		/**
		 * Sets the channel Uri
		 *
		 * @param channelUri The channel Uri associated with the underlying service.
		 */
		public function set channelUri(channelUri:String):void
		{
			if (StringUtils.isEmpty(channelUri))
			{
				log.error("Setting the channel Uri to an empty or null string.");
				return;
			}
			_service.serviceChannelUri=channelUri;
		}

		/**
		 * Calls a remote service Java method.
		 *
		 * @param methodName Java method name to be invoked.
		 * @param arguments method arguments.
		 * @param callBack <code>function(ev:OperationReturnEvent)</code>
		 * @param callContext object that needs to be passed back to caller via callback.
		 */
		public function callService(methodName:String, arguments:Array, callback:Function=null, callContext:Object=null):void
		{

			if (_enabled)
			{
				var operation:AbstractOperation=_service[methodName];
				var opContext:Object={ //
						orgCallback: callback, //
						orgCallContext: callContext //
					};
				_operationInvocation.invoke(operation, arguments, onInvocationComplete, opContext);
			}
			else
			{
				if (callback != null)
				{
					var returnEvent:OperationReturnEvent=OperationReturnEvent.newResultEvent(null, callContext);
					callback(returnEvent);
				}
			}


		}

		/**
		 * Invokes the supplied callback with callContext and unpacked result.
		 */
		protected function result(event:ResultEvent, callback:Function, callContext:Object):void
		{
			if (callback == null || event == null)
			{
				return;
			}
			// Create ResultEvent and call the callback function
			var returnEvent:OperationReturnEvent=OperationReturnEvent.newResultEvent(event.result, callContext);
			if (_resultHandler != null)
			{
				_resultHandler.handleResult(returnEvent, this);
			}
			notify(callback, returnEvent);
		}

		/**
		 * Invokes the supplied callback with callContext and unpacked fault.
		 */
		protected function fault(event:FaultEvent, callback:Function, callContext:Object):void
		{
			if (callback == null || event == null)
			{
				return;
			}
			// Create ErrorEvent and call the callback function
			var returnEvent:OperationReturnEvent=OperationReturnEvent.newErrorEvent(event.fault, callContext);
			if (_resultHandler != null)
			{
				_resultHandler.handleResult(returnEvent, this);
			}
			notify(callback, returnEvent);
		}

		/**
		 * Notifies the caller with the given result event.
		 */
		protected function notify(callback:Function, event:OperationReturnEvent):void
		{
			callback(event);
		}

		/**
		 * Handles response of the invocation.
		 */
		private function onInvocationComplete(event:Event, opContext:Object):void
		{
			var origCallback:Function=opContext.orgCallback;
			var origCallContext:Object=opContext.orgCallContext;
			if (event is ResultEvent)
			{
				result(event as ResultEvent, origCallback, origCallContext);
			}
			else if (event is FaultEvent)
			{
				fault(event as FaultEvent, origCallback, origCallContext);
			}
		}
	}
}